import { genSearchArray, findPoint } from "./genSearchArray";
import Point from "./Point";

interface inputTemplate {
    start: Pick<Point, "x" | "y">;
    end: Pick<Point, "x" | "y">;
    isWall: (point: Point) => boolean;
    sortPoint?: (a: Point, b: Point) => number;
    findPoint?: (set: Point[], point: Point) => boolean;
    search?: typeof genSearchArray;
}
/** 二维 A* 寻路算法 */
export function FindPath({
    start: { x: x1, y: y1 },
    end: { x: x2, y: y2 },
    isWall,
    sortPoint = (a: Point, b: Point) => {
        return a.f - b.f;
    },
    search = genSearchArray,
}: inputTemplate) {
    const startPoint = new Point(x1, y1);
    const endPoint = new Point(x2, y2);
    const openSet: Point[] = [startPoint];
    const pathSet: Point[] = [];
    const closeSet: Point[] = [];

    if (!startPoint) {
        throw "未设置起点";
    }
    if (!endPoint) {
        throw "未设置终点";
    }
    if (isWall(endPoint)) {
        console.warn("终点在墙里!");
        return pathSet;
    }

    while (openSet.length > 0) {
        openSet.sort(sortPoint);
        let n = openSet.shift()!;
        closeSet.push(n);

        //该点为终点
        if (n.equals(endPoint)) {
            let cur = n;
            // 一个循环找到这条路径
            while (cur.parent) {
                pathSet.unshift(cur);
                cur = cur.parent;
            }
            return pathSet;
        }

        // 该点非终点，进行方位遍历查找
        search(n).forEach(([x, y]) => {
            let m = new Point(x, y);
            if (m.equals(n) || isWall(m) || findPoint(closeSet, m)) {
                return;
            }
            if (findPoint(openSet, m)) {
                let newG = n.g + n.calcG(m, n);
                if (newG < m.g) {
                    m.g = newG;
                    m.f = m.g + m.h;
                    m.parent = n;
                }
            } else {
                m.parent = n;
                m.initF(endPoint);
                openSet.push(m);
            }
        });
    }
    if (pathSet.length === 0) {
        console.log("无法找到路径");
    }
    return pathSet;
}
